// miniloop, a simple live looper
//
// Copyright (C) 2007 Nick Thomas
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY NICK THOMAS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL NICK THOMAS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <gtkmm/main.h>
#include "Config.hpp"
#include "Icons.hpp"
#include "LoopWindow.hpp"
using namespace miniloop;

LoopButton::LoopButton(LoopWindow *window, int id)
{
    m_window = window;
    m_id = id;

    std::list<Gtk::TargetEntry> dnd_targets;
    dnd_targets.push_back(Gtk::TargetEntry("STRING"));
    dnd_targets.push_back(Gtk::TargetEntry("text/plain"));

    drag_dest_set(dnd_targets);
    signal_drag_data_received().
	connect(sigc::mem_fun(*this, &LoopButton::on_drop));

    set_size_request(g_config.loop_width, g_config.loop_height);
}

void LoopButton::on_drop(const Glib::RefPtr<Gdk::DragContext> &context, int x,
    int y, const Gtk::SelectionData &data, guint info, guint time)
{
    Glib::ustring file = data.get_data_as_string();
    m_window->load_loop(m_id, file);

    Glib::ustring::size_type name_start = file.find_last_of('/');
    Glib::ustring::size_type name_end = file.find_last_of('.');

    if (name_start == Glib::ustring::npos) {
	name_start = 0;
    } else {
	name_start++;
    }
    if (name_end == Glib::ustring::npos || name_end < name_start)
	name_end = file.length();

    Glib::ustring name = file.substr(name_start, name_end - name_start);

    set_label(name);

    context->drag_finish(false, false, time);
}

LoopWindow::LoopWindow(Engine &engine)
    : m_engine(engine)
{
    m_beat_icon_inactive = Gdk::Pixbuf::create_from_xpm_data(
	    Icons::beat_indicator_inactive);
    m_beat_icon_bar = Gdk::Pixbuf::create_from_xpm_data(
	    Icons::beat_indicator_bar);
    m_beat_icon_beat = Gdk::Pixbuf::create_from_xpm_data(
	    Icons::beat_indicator_beat);
}

void LoopWindow::init()
{
    m_buttons.resize(g_config.num_loops);
    m_button_rows.resize(g_config.loop_rows);

    int button = 0;
    for (int row = 0; row < g_config.loop_rows; row++) {
	m_button_rows[row] = new Gtk::HBox;
	m_button_box.pack_start(*m_button_rows[row], Gtk::PACK_SHRINK);

	for (int col = 0; col < g_config.loop_cols; col++) {
	    m_buttons[button] = new LoopButton(this, button);
	    m_button_rows[row]->pack_start(*m_buttons[button]);
	    button++;
	}
    }

    Glib::signal_timeout().connect(sigc::bind(
        sigc::mem_fun(*this, &LoopWindow::update_playback_indicators), 0), 20);

    m_time_slider.set_range(0, g_config.loop_length - 1);
    m_time_slider.set_draw_value(false);

    m_beat_indicator.set(m_beat_icon_inactive);

    m_playback_strip.pack_start(m_beat_indicator, Gtk::PACK_SHRINK);
    m_playback_strip.pack_start(m_time_slider);

    m_vbox.pack_start(m_button_box);
    m_vbox.pack_start(m_playback_strip);
    add(m_vbox);

    set_title("miniloop - loops");
    set_deletable(false);
    show_all_children();
}

void LoopWindow::load_loop(int loop, Glib::ustring path)
{
    Glib::ustring error;

    if (!m_engine.load_loop(loop, path, error)) {
	printf("Error loading %s: %s\n", path.c_str(), error.c_str());
    }
}

bool LoopWindow::update_playback_indicators(int timer)
{
    int playback_frame = m_engine.get_playback_frame();

    m_time_slider.set_value(playback_frame);

    int samples_per_half_beat = g_config.loop_length /
	(2 * g_config.num_bars * g_config.beats_per_bar);
    int cur_half_beat = playback_frame / samples_per_half_beat;

    if (cur_half_beat % (2 * g_config.beats_per_bar) == 0) {
	m_beat_indicator.set(m_beat_icon_bar);
    } else if (cur_half_beat % 2 == 0) {
	m_beat_indicator.set(m_beat_icon_beat);
    } else {
	m_beat_indicator.set(m_beat_icon_inactive);
    }

    return true;
}
